---
description: How to monitor your GraphQL server using Prometheus.
---

import { Callout } from '@theguild/components'

# Monitoring

Once in production, it's important to monitor your GraphQL server.

A common way to monitor your server is by using Prometheus, a popular, free and open-source
monitoring system.

## Getting Started

To get started, you need to install the `@graphql-yoga/plugin-prometheus` package.

This plugin tracks the complete execution flow, and reports metrics using Prometheus tracing (based
on `prom-client`).

```sh npm2yarn
npm i prom-client @graphql-yoga/plugin-prometheus
```

You can then add the plugin to your GraphQL server:

```ts
import { createYoga } from 'graphql-yoga'
import { usePrometheus } from '@graphql-yoga/plugin-prometheus'

const yoga = createYoga({
  plugins: [
    schema: mySchema, // Provide your GraphQL schema
    usePrometheus({
      endpoint: '/metrics', // optional, default is `/metrics`, you can disable it by setting it to `false` if registry is configured in "push" mode
      // Optional, see default values below
      metrics: {
        // By default, these are the metrics that are enabled:
        graphql_envelop_request_time_summary: true,
        graphql_envelop_phase_parse: true,
        graphql_envelop_phase_validate: true,
        graphql_envelop_phase_context: true,
        graphql_envelop_phase_execute: true,
        graphql_envelop_phase_subscribe: true,
        graphql_envelop_error_result: true,
        graphql_envelop_deprecated_field: true,
        graphql_envelop_request_duration: true,
        graphql_envelop_schema_change: true,
        graphql_envelop_request: true,
        graphql_yoga_http_duration: true,

        // This metric is disabled by default.
        // Warning: enabling resolvers level metrics will introduce significant overhead
        graphql_envelop_execute_resolver: false
      }
    })
  ]
})

const server = createServer(yoga)
server.listen(4000, () => {
  console.log(`Server is running on http://localhost:4000/${server.graphqlEndpoint}`)
})
```

<Callout type="warning">
  Tracing resolvers using `resolvers: true` might have a performance impact on your GraphQL runtime.
  Please consider to test it locally first and then decide if it's needed.
</Callout>

Now, you can access the metrics by visiting the `/metrics` endpoint of your server.

You can then configure Prometheus to scrape the metrics from your server by using this URL.

## Available Metrics

All metrics are disabled by default. You can enable the one you are interested in by setting the
corresponding key in the `metric` option object to `true`. You can also provide a string to
customize the metric name, or an object to provide more options by using `createHistogram`,
`createCounter` and `createSummary` (see
[`siimon/prom-client` documentation](https://github.com/siimon/prom-client#custom-metrics)).
Histogram metrics can be passed an array of numbers to configure buckets.

Each metric also expose a set of labels. All labels are exposed by default but can be separately
disabled by setting the corresponding key in `labels` option object to `false`.

### `graphql_yoga_http_duration`

> ✅ This metric is enabled by default.

This metric tracks the duration of incoming (downstream) HTTP requests. It reports the time spent to
process each incoming request as a
[histogram](https://prometheus.io/docs/concepts/metric_types/#histogram).

It is useful to track the responsiveness of your gateway. A spike in this metric could indicate a
performance issue and that further investigation is needed.

Please note that this metric is not specific to GraphQL, it tracks all incoming HTTP requests.

You can use labels to have a better understanding of the requests and group them together. A common
filter is to include only `statusCode` with `200` value and `method` with `POST` (the default method
for GraphQL requests, but it can also be `GET` depending on your client setup) value to get
execution time of successful GraphQL requests only.

#### Labels

This metric includes some useful labels to help you identify requests and group them together.

| Label           | Description                                                                                                                                                                                                                                                                                                                                                                             |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `method`        | The HTTP method used to request the gateway endpoint.<br/><br/> Since GraphQL usually only uses `POST` requests, this can be used to filter out GraphiQL-related requests. <br/><br/> It can be any HTTP verb, including disallowed ones. Which means this metric can also be used to track malformed or malicious requests.                                                            |
| `statusCode`    | The HTTP status code returned by the gateway.<br/><br/>You probably want to filter out non-`200` responses to have a view of the successful requests.<br/><br/>This can help you identify which requests are failing and why. Since GraphQL errors are returned as `200 OK` responses, this can be useful to track errors that are not related to the GraphQL, like malformed requests. |
| `operationName` | If available, the name of the GraphQL operation requested, otherwise `Anonymous`.<br/><br/>This can help you identify which operations are slow or failing.<br/><br/>We recommend you always provide an operation name to your queries and mutations to help performance analysis and bug tracking.                                                                                     |
| `operationType` | The type of the GraphQL operation requested. It can be one of `query`, `mutation`, or `subscription`.<br/><br/>This can help you differentiate read and write performance of the system. It can for example help understand cache impact.                                                                                                                                               |
| `url`           | The URL of the request. Useful to filter graphql endpoint metrics (`/graphql` by default).                                                                                                                                                                                                                                                                                              |

#### Sample Output

```text
# HELP graphql_yoga_http_duration Time spent on HTTP connection
# TYPE graphql_yoga_http_duration histogram
graphql_yoga_http_duration_bucket{le="0.005",method="GET",statusCode="200",operationName="Anonymous"} 1
graphql_yoga_http_duration_bucket{le="0.01",method="GET",statusCode="200",operationName="Anonymous"} 1
graphql_yoga_http_duration_bucket{le="0.025",method="GET",statusCode="200",operationName="Anonymous"} 1
graphql_yoga_http_duration_bucket{le="0.05",method="GET",statusCode="200",operationName="Anonymous"} 1
graphql_yoga_http_duration_bucket{le="0.1",method="GET",statusCode="200",operationName="Anonymous"} 1
graphql_yoga_http_duration_bucket{le="0.25",method="GET",statusCode="200",operationName="Anonymous"} 1
graphql_yoga_http_duration_bucket{le="0.5",method="GET",statusCode="200",operationName="Anonymous"} 1
graphql_yoga_http_duration_bucket{le="1",method="GET",statusCode="200",operationName="Anonymous"} 4
graphql_yoga_http_duration_bucket{le="2.5",method="GET",statusCode="200",operationName="Anonymous"} 4
graphql_yoga_http_duration_bucket{le="5",method="GET",statusCode="200",operationName="Anonymous"} 4
graphql_yoga_http_duration_bucket{le="10",method="GET",statusCode="200",operationName="Anonymous"} 4
graphql_yoga_http_duration_bucket{le="+Inf",method="GET",statusCode="200",operationName="Anonymous"} 4
graphql_yoga_http_duration_sum{method="GET",statusCode="200",operationName="Anonymous"} 3
graphql_yoga_http_duration_count{method="GET",statusCode="200",operationName="Anonymous"} 4
```

### `graphql_envelop_phase_parse`

> ✅ This metric is enabled by default.

This metric tracks the duration of the `parse` phase of the GraphQL execution. It reports the time
spent parsing the incoming GraphQL operation.

It is reported as a [histogram](https://prometheus.io/docs/concepts/metric_types/#histogram).

Since you don't have control over the parsing phase, this metric is mostly useful to track potential
attacks. A spike in this metric could indicate someone is trying to send malicious operations to
your gateway.

#### Labels

| Label           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`. |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.     |

#### Sample Output

```text
# HELP graphql_envelop_phase_parse Time spent on running GraphQL "parse" function
# TYPE graphql_envelop_phase_parse histogram
graphql_envelop_phase_parse_bucket{le="0.005",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="0.01",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="0.025",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="0.05",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="0.1",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="0.25",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="0.5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="1",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="2.5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="10",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_bucket{le="+Inf",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_parse_sum{operationName="Anonymous",operationType="query"} 0.001
graphql_envelop_phase_parse_count{operationName="Anonymous",operationType="query"} 1
```

### `graphql_envelop_phase_validate`

> ✅ This metric is enabled by default.

This metric tracks the duration of the `validate` phase of the GraphQL execution. It reports the
time spent validating the incoming GraphQL operation.

It is reported as a [histogram](https://prometheus.io/docs/concepts/metric_types/#histogram).

#### Labels

| Label           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`. |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.     |

#### Sample Output

```text
# HELP graphql_envelop_phase_validate Time spent on running GraphQL "validate" function
# TYPE graphql_envelop_phase_validate histogram
graphql_envelop_phase_validate_bucket{le="0.005",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="0.01",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="0.025",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="0.05",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="0.1",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="0.25",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="0.5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="1",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="2.5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="10",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_bucket{le="+Inf",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_validate_sum{operationName="Anonymous",operationType="query"} 0.004
graphql_envelop_phase_validate_count{operationName="Anonymous",operationType="query"} 1
```

### `graphql_envelop_phase_context`

> ✅ This metric is enabled by default.

This metric tracks the duration of the `context` phase of the GraphQL execution. It reports the time
spent building the context object that will be passed to the executors.

It is reported as a [histogram](https://prometheus.io/docs/concepts/metric_types/#histogram).

#### Labels

| Label           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`. |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.     |

#### Sample Output

```text
# HELP graphql_envelop_phase_context Time spent on building the GraphQL context
# TYPE graphql_envelop_phase_context histogram
graphql_envelop_phase_context_bucket{le="0.005",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="0.01",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="0.025",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="0.05",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="0.1",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="0.25",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="0.5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="1",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="2.5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="10",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_bucket{le="+Inf",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_context_sum{operationName="Anonymous",operationType="query"} 0
graphql_envelop_phase_context_count{operationName="Anonymous",operationType="query"} 1
```

### `graphql_envelop_phase_execute`

> ✅ This metric is enabled by default.

This metric tracks the duration of the `execute` phase of the GraphQL execution. It reports the time
spent actually resolving the response of the incoming operation. This includes the gathering of all
the data from all sources required to construct the final response. It is reported as a
[histogram](https://prometheus.io/docs/concepts/metric_types/#histogram).

It is the metric that will give you the most insights into the performance of your own code, since
this is where most of the work from your code (resolvers) is done.

#### Labels

| Label           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`. |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.     |

#### Sample Output

```text
# HELP graphql_envelop_phase_execute Time spent on running the GraphQL "execute" function
# TYPE graphql_envelop_phase_execute histogram
graphql_envelop_phase_execute_bucket{le="0.005",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="0.01",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="0.025",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="0.05",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="0.1",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="0.25",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="0.5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="1",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="2.5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="5",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="10",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_bucket{le="+Inf",operationName="Anonymous",operationType="query"} 1
graphql_envelop_phase_execute_sum{operationName="Anonymous",operationType="query"} 0.002
graphql_envelop_phase_execute_count{operationName="Anonymous",operationType="query"} 1
```

### `graphql_envelop_phase_subscribe`

> ✅ This metric is enabled by default.

This metric tracks the duration of the `subscribe` phase of the GraphQL execution. It reports the
time spent initiating a subscription (which doesn't include actually sending the first response).

It is reported as a [histogram](https://prometheus.io/docs/concepts/metric_types/#histogram).

#### Labels

| Label           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`. |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.     |

#### Sample Output

```text
# HELP graphql_envelop_phase_subscribe Time spent on running the GraphQL "subscribe" function
# TYPE graphql_envelop_phase_subscribe histogram
graphql_envelop_phase_subscribe_bucket{le="0.005",operationName="Anonymous",operationType="subscription"} 0
graphql_envelop_phase_subscribe_bucket{le="0.01",operationName="Anonymous",operationType="subscription"} 0
graphql_envelop_phase_subscribe_bucket{le="0.025",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_phase_subscribe_bucket{le="0.05",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_phase_subscribe_bucket{le="0.1",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_phase_subscribe_bucket{le="0.25",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_phase_subscribe_bucket{le="0.5",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_phase_subscribe_bucket{le="1",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_phase_subscribe_bucket{le="2.5",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_phase_subscribe_bucket{le="5",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_phase_subscribe_bucket{le="10",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_phase_subscribe_bucket{le="+Inf",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_phase_subscribe_sum{operationName="Anonymous",operationType="subscription"} 0.011
graphql_envelop_phase_subscribe_count{operationName="Anonymous",operationType="subscription"} 1
```

### `graphql_envelop_request_duration`

> ✅ This metric is enabled by default.

This metric tracks the duration of the complete GraphQL operation execution.

It is reported as a [histogram](https://prometheus.io/docs/concepts/metric_types/#histogram).

#### Labels

| Label           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`. |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.     |

#### Sample Output

```text
# HELP graphql_envelop_request_duration Time spent on running the GraphQL operation from parse to execute
# TYPE graphql_envelop_request_duration histogram
graphql_envelop_request_duration_bucket{le="0.005",operationName="Anonymous",operationType="subscription"} 0
graphql_envelop_request_duration_bucket{le="0.01",operationName="Anonymous",operationType="subscription"} 0
graphql_envelop_request_duration_bucket{le="0.025",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_request_duration_bucket{le="0.05",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_request_duration_bucket{le="0.1",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_request_duration_bucket{le="0.25",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_request_duration_bucket{le="0.5",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_request_duration_bucket{le="1",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_request_duration_bucket{le="2.5",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_request_duration_bucket{le="5",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_request_duration_bucket{le="10",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_request_duration_bucket{le="+Inf",operationName="Anonymous",operationType="subscription"} 1
graphql_envelop_request_duration_sum{operationName="Anonymous",operationType="subscription"} 0.011
graphql_envelop_request_duration_count{operationName="Anonymous",operationType="subscription"} 1
```

### `graphql_envelop_request_time_summary`

> ✅ This metric is enabled by default.

This metric provides a summary of the time spent on the GraphQL operation execution.

It reports the same timing than
[`graphql_envelop_request_duration`](#graphql_envelop_request_duration) but as a
[summary](https://prometheus.io/docs/concepts/metric_types/#summary).

#### Labels

| Label           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`. |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.     |

#### Sample output

```text
# HELP graphql_envelop_request_time_summary Summary to measure the time to complete GraphQL operations
# TYPE graphql_envelop_request_time_summary summary
graphql_envelop_request_time_summary{quantile="0.01",operationName="Anonymous",operationType="subscription"} 0.022
graphql_envelop_request_time_summary{quantile="0.05",operationName="Anonymous",operationType="subscription"} 0.022
graphql_envelop_request_time_summary{quantile="0.5",operationName="Anonymous",operationType="subscription"} 0.022
graphql_envelop_request_time_summary{quantile="0.9",operationName="Anonymous",operationType="subscription"} 0.022
graphql_envelop_request_time_summary{quantile="0.95",operationName="Anonymous",operationType="subscription"} 0.022
graphql_envelop_request_time_summary{quantile="0.99",operationName="Anonymous",operationType="subscription"} 0.022
graphql_envelop_request_time_summary{quantile="0.999",operationName="Anonymous",operationType="subscription"} 0.022
graphql_envelop_request_time_summary_sum{operationName="Anonymous",operationType="subscription"} 0.022
graphql_envelop_request_time_summary_count{operationName="Anonymous",operationType="subscription"} 1
```

### `graphql_envelop_error_result`

> ✅ This metric is enabled by default.

This metric tracks the number of errors that occurred returned by the GraphQL execution. It counts
all errors found in the final response, but it also includes errors from other GraphQL processing
phases (parsing, validation and context building).

It is exposed as a [counter](https://prometheus.io/docs/concepts/metric_types/#counter).

#### Labels

Depending on the phase when the error occurred, some labels may be missing. For example, if the
error occurred during the context phase, only the `phase` label will be present.

| Label           | Description                                                                                                                                                           |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `path`          | The path of the field that caused the error. It can be `undefined` if the error is not related to a given field.                                                      |
| `phase`         | The phase of the GraphQL execution where the error occurred. It can be `parse`, `validate`, `context`, `execute` (for every operation types including subscriptions). |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`.                                                               |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.                                                                   |

#### Sample Output

```text
# HELP graphql_envelop_error_result Counts the amount of errors reported from all phases
# TYPE graphql_envelop_error_result counter
graphql_envelop_error_result{operationName="Anonymous",operationType="query",path="undefined",phase="execute"} 1
```

### `graphql_envelop_request`

> ✅ This metric is enabled by default.

This metric tracks the number of GraphQL operations executed. It counts all operations, either
failed or successful, including subscriptions.

It is exposed as a [counter](https://prometheus.io/docs/concepts/metric_types/#counter).

#### Labels

| Label           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`. |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.     |

#### Sample output

```text
# HELP graphql_envelop_request Counts the amount of GraphQL requests executed through Envelop
# TYPE graphql_envelop_request counter
graphql_envelop_request{operationName="Anonymous",operationType="query"} 1
```

### `graphql_envelop_deprecated_field`

> ✅ This metric is enabled by default.

This metric tracks the number of deprecated fields used in the GraphQL operation.

#### Labels

| Label           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| `fieldName`     | The name of the deprecated field that has been used.                                                    |
| `typeName`      | The name of the parent type of the deprecated field that has been used.                                 |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`. |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.     |

#### Sample Output

```text
# HELP graphql_envelop_deprecated_field Counts the amount of deprecated fields used in selection sets
# TYPE graphql_envelop_deprecated_field counter
graphql_envelop_deprecated_field{operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query"} 1
```

### `graphql_envelop_schema_change`

> ✅ This metric is enabled by default.

This metric tracks the number of schema changes that have occurred since the gateway started.

If you are using a plugin that modifies the schema on the fly, be aware that this metric will also
include updates made by those plugins. Which means that one schema update can actually trigger
multiple schema changes.

#### Labels

This metric does not include any labels.

#### Sample output

```text
# HELP graphql_envelop_schema_change Counts the amount of schema changes
# TYPE graphql_envelop_schema_change counter
graphql_envelop_schema_change 1
```

### `graphql_envelop_execute_resolver`

> ❌ This metric is disabled by default.

> **Caution**: Enabling resolvers level metrics will introduce significant overhead.
>
> We highly recommend to enable this for debugging purpose only.

This metric tracks the duration of each resolver execution. It reports the time spent only on
additional resolvers, not on fields that are resolved by a subgraph. It is up to the subgraph server
to implement resolver level metrics, the gateway can't remotely track their execution time.

#### Filter resolvers to instrument

To mitigate the cost of instrumenting all resolvers, you can explicitly list the fields that should
be instrumented by providing a list of field names to the `instrumentResolvers` option.

It is a list of strings in the form of `TypeName.fieldName`. For example, to instrument the `hello`
root query, you would use `Query.hello`.

You can also use wildcards to instrument all the fields for a type. For example, to instrument all
root queries, you would use `Query.*`.

#### Labels

| Label           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| `operationType` | The type of the GraphQL operation requested. This can be one of `query`, `mutation`, or `subscription`. |
| `operationName` | The name of the GraphQL operation requested. It will be `Anonymous` if no `operationName` is found.     |
| `fieldName`     | The name of the field being resolved.                                                                   |
| `typeName`      | The name of the parent type of the field being resolved.                                                |
| `returnType`    | The name of the return type of the field being resolved.                                                |

#### Sample output

```text
# HELP graphql_envelop_execute_resolver Time spent on running the GraphQL resolvers
# TYPE graphql_envelop_execute_resolver histogram
graphql_envelop_execute_resolver_bucket{le="0.005",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="0.01",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="0.025",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="0.05",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="0.1",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="0.25",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="0.5",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="1",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="2.5",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="5",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="10",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_bucket{le="+Inf",operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
graphql_envelop_execute_resolver_sum{operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 0
graphql_envelop_execute_resolver_count{operationName="Anonymous",operationType="query",fieldName="hello",typeName="Query",returnType="String!"} 1
```

## Configuration

### Custom registry

You can customize the `prom-client` `Registry` object if you are using a custom one, by passing it
along with the configuration object:

```ts
import { execute, parse, specifiedRules, subscribe, validate } from 'graphql'
import { Registry } from 'prom-client'
import { envelop, useEngine } from '@envelop/core'

const myRegistry = new Registry()

const getEnveloped = envelop({
  plugins: [
    useEngine({ parse, validate, specifiedRules, execute, subscribe }),
    // ... other plugins ...
    usePrometheus({
      // ... config ...
      registry: myRegistry
    })
  ]
})
```

> Note: if you are using custom `prom-client` instances, you need to make sure to pass your registry
> there as well.

### Introspection

If you wish to disable introspection logging, you can use `skipIntrospection: true` in your config
object.

### Customize metrics options and labels

Each metric can be configured and custom `labels` can be added to provide more metadata. You can
create a custom extraction function for every `Histogram` / `Summary` / `Counter`:

```ts
import { execute, parse, specifiedRules, subscribe, validate } from 'graphql'
import { Histogram, register as registry } from 'prom-client'
import { envelop, useEngine } from '@envelop/core'
import { createHistogram, usePrometheus } from '@envelop/prometheus'

const getEnveloped = envelop({
  plugins: [
    useEngine({ parse, validate, specifiedRules, execute, subscribe }),
    // ... other plugins ...
    usePrometheus({
      metrics: {
        graphql_envelop_phase_parse: createHistogram({
          registry: registry // make sure to add your custom registry, if you are not using the default one
          histogram: new Histogram({
            name: 'my_custom_name',
            help: 'HELP ME',
            labelNames: ['opText'] as const,
          }),
          fillLabelsFn: params => {
            // if you wish to fill your `labels` with metadata, you can use the params in order to get access to things like DocumentNode, operationName, operationType, `error` (for error metrics) and `info` (for resolvers metrics)
            return {
              opText: print(params.document)
            }
          }
        })
      }
    })
  ]
})
```

### Mitigate hight volume of exported metrics

In some cases, the large variety of label values can lead to a huge amount of metrics being
exported. To save bandwidth or storage, you can reduce the amount of reported metrics by multiple
ways.

#### Monitor only some phases

Some metrics observe events in multiple phases of the graphql pipeline. The metric with the highest
chance causing large amount of metrics is `graphql_envelop_error_result`, because it can contain
information specific to the error reported.

You can lower the amount of reported errors by changing the phases monitored by this metric.

```ts
import { execute, parse, specifiedRules, subscribe, validate } from 'graphql'
import { Registry } from 'prom-client'
import { envelop, useEngine } from '@envelop/core'

const myRegistry = new Registry()

const getEnveloped = envelop({
  plugins: [
    useEngine({ parse, validate, specifiedRules, execute, subscribe }),
    usePrometheus({
      metrics: {
        // To ignore parsing and validation error, and only monitor errors happening during
        // resolvers executions, you can enable only the `execute` and `subscribe` phases
        graphql_envelop_error_result: ['execute', 'subscribe']
      }
    })
  ]
})
```

#### Skip observation based on request context

To save bandwidth or storage, you can reduce the amount of reported values by filtering which events
are observed based on the request context.

For example, you can only monitor a subset of operations, because they are critical or that you want
to debug it's performance:

```ts
import { execute, parse, specifiedRules, subscribe, validate } from 'graphql'
import { envelop, useEngine } from '@envelop/core'
import { usePrometheus } from '@envelop/prometheus'

const TRACKED_OPERATION_NAMES = [
  // make a list of operation that you want to monitor
]

const getEnveloped = envelop({
  plugins: [
    useEngine({ parse, validate, specifiedRules, execute, subscribe }),
    usePrometheus({
      metrics: {
        graphql_yoga_http_duration: createHistogram({
          registry,
          histogram: {
            name: 'graphql_yoga_http_duration',
            help: 'Time spent on HTTP connection',
            labelNames: ['operation_name']
          },
          fillLabelsFn: ({ operationName }, _rawContext) => ({
            operation_name: operationName
          }),
          shouldObserve: context => TRACKED_OPERATIONS.includes(context?.params?.operationName)
        })
      }
    })
  ]
})
```

## Caveats

Due to Prometheus client API limitations, if this plugin is initialized multiple times, only the
metrics configuration of the first initialization will be applied.

If necessary, use a different registry instance for each plugin instance, or clear the registry
before plugin initialization.

```ts
function usePrometheusWithRegistry() {
  // create a new registry instance for each plugin instance
  const registry = new Registry()

  // or just clear the registry if you use only on plugin instance at a time
  registry.clear()

  return usePrometheus({
    registry
    // ...
  })
}
```

Keep in mind that this implies potential data loss in pull mode if some data is produced between
last pull and the re-initialization of the plugin.
